package com.pkk.spring.cloud.service.gateway.route;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

/**
 * 路由MYSQL的仓库
 * <p>
 * 交给子类去实现
 *
 * @author peikunkun
 * @version V1.0
 * @date 2021-01-15 18:19
 **/
@AutoConfigureBefore(GatewayAutoConfiguration.class)
public class InMysqlRouteDefinitionRepository extends AdviceRouteDefinitionRepository {

  @Autowired
  private JdbcTemplate jdbcTemplate;


  /**
   * 默认的表名称
   */
  protected String DEFAULT_GATEWAY_TABLENAME = "p_sys_gateway";

  //创建表的脚本
  protected String DEFAULT_GATEWAY_CREATE_TABLE = ""
      + "Create Table If Not Exists `p_sys_gateway` (\n"
      + "  `id` varchar(160) NOT NULL COMMENT '主键ID',\n"
      + "  `uri` varchar(1024) NOT NULL COMMENT '服务路由地址，example:http://localhost:8001|lb://spring-cloud-search-runner',\n"
      + "  `order` int(11) NOT NULL DEFAULT '0' COMMENT '路由顺序',\n"
      + "  `predicates` text NOT NULL COMMENT '断言，路径匹配的进行路由[{\"name\": \"Path\",\"args\": {\"pattern\": \"/spring-cloud-web-runner/**\"}}]',\n"
      + "  `filters` varchar(2048) NOT NULL DEFAULT '[]' COMMENT '过滤,example:[{\"args\":{\"_genkey_0\":\"1\"},\"name\":\"StripPrefix\"},\"name\":\"StripPrefix\"}]',\n"
      + "  `metadata` varchar(2048) NOT NULL DEFAULT '{}' COMMENT '元数据',\n"
      + "  `status` tinyint(2) unsigned DEFAULT NULL COMMENT '状态:1:启用,0:不启用',\n"
      + "  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n"
      + "  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n"
      + "  PRIMARY KEY (`id`),\n"
      + "  KEY `nm_key` (`id`,`order`,`status`)\n"
      + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";


  /**
   * 删除指定路由{伪删除}
   */
  @SuppressWarnings("all")
  protected String DEFAULT_DELETE_GATEWAY = ""
      + "UPDATE `p_sys_gateway` SET `STATUS`=0 WHERE id = ?";

  /**
   * 增加指定路由
   */
  @SuppressWarnings("all")
  protected String DEFAULT_ADD_GATEWAY = ""
      + "INSERT INTO `p_sys_gateway`(`id`, `uri`, `order`, `predicates`, `filters`, `metadata`) VALUES (?,?,?,?,?,?)";


  /**
   * 获取所有有用的路由
   */
  @SuppressWarnings("all")
  protected String DEFAULT_FIND_GATEWAY = ""
      + "select `id`, `uri`, `order`, `predicates`, `filters`, `metadata` from `p_sys_gateway` WHERE `STATUS` = 1 ORDER BY `order` ASC";


  /**
   * 初始化动态路由
   *
   * @author peikunkun
   * @date 2021/1/23 0023 上午 10:43
   */
  @Override
  public void init() {
    this.getRouteDefinitionList();
  }

  /**
   * 获取路由定义信息
   *
   * @return java.util.List<org.springframework.cloud.gateway.route.RouteDefinition>
   * @Param
   * @author peikunkun
   * @date 2021/1/23 0023 上午 10:38
   * @since
   */
  @Override
  protected List<RouteDefinition> getRouteDefinitionList() {
    List<RouteDefinition> routeDefinitions = new ArrayList<>();
    final List<RouteDefinition> query = jdbcTemplate
        .query(DEFAULT_FIND_GATEWAY, new BeanPropertyRowMapper(RouteDefinition.class));
    query.stream().forEach(
        routeDefinition -> routeDefinitions.add(JSONUtil.toBean(routeDefinition.toString(), RouteDefinition.class)));
    return routeDefinitions;
  }

  /**
   * 更新路由
   *
   * @param definitions
   * @return boolean
   * @Param definitions
   * @author peikunkun
   * @date 2021/1/22 0022 下午 6:05
   * @since
   */
  @Override
  public boolean update(List<RouteDefinition> definitions) {
    definitions.stream().forEach(definition -> {
      this.delete(Mono.just(definition.getId())).subscribe();
      this.save(Mono.just(definition)).subscribe();
    });
    //通知
    super.notifyChanged();
    return true;
  }


  @Override
  public Mono<Void> save(Mono<RouteDefinition> route) {
    final Mono<Void> id_may_not_be_empty = route.flatMap(r -> {
      if (StringUtils.isEmpty(r.getId())) {
        return Mono.error(new IllegalArgumentException("id may not be empty"));
      }

      if (StringUtils.isEmpty(r.getId())) {
        return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition [ID] not found: " + r.getId())));
      }

      if (StringUtils.isEmpty(r.getUri())) {
        return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition [URI] not found: " + r.getUri())));
      }

      if (CollectionUtil.isEmpty(r.getPredicates())) {
        r.setPredicates(new ArrayList());
      }

      if (CollectionUtil.isEmpty(r.getFilters())) {
        r.setFilters(new ArrayList());
      }

      if (null == r.getMetadata()) {
        r.setMetadata(new HashMap<>());
      }

      final int update = jdbcTemplate.update(DEFAULT_ADD_GATEWAY, r.getId(), r.getUri(), r.getOrder(),
          r.getPredicates(), r.getFilters(), r.getMetadata());
      return Mono.empty();
    });
    id_may_not_be_empty.subscribe();
    //通知
    super.notifyChanged();
    return id_may_not_be_empty;
  }

  @Override
  public Mono<Void> delete(Mono<String> routeId) {
    final Mono<Void> objectMono = routeId.flatMap(id -> {
      if (jdbcTemplate.update(DEFAULT_DELETE_GATEWAY, id) >= 1) {
        return Mono.empty();
      }
      return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
    });
    //生效
    objectMono.subscribe();
    //通知
    super.notifyChanged();
    return objectMono;
  }
}
